package com.personal.dataanalyse.reportmanage.xml;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.personal.core.utils.Assert;
import com.personal.core.utils.CoreUtil;
import com.personal.core.xml.XMLDoc;
import com.personal.dataanalyse.enums.EchartTypeEnum;
import com.personal.dataanalyse.reportmanage.base.DataAnalyseModel;
import com.personal.dataanalyse.reportmanage.base.DataChartModel;
import com.personal.dataanalyse.reportmanage.base.DimensionInfo;
import com.personal.dataanalyse.reportmanage.base.QuotaInfo;
import com.personal.dataanalyse.reportmanage.model.BarLineEChartModel;
import com.personal.dataanalyse.reportmanage.model.EChartModel;
import com.personal.dataanalyse.reportmanage.model.PieEChartModel;
import com.personal.dataanalyse.reportmanage.model.ReportDimension;
import com.personal.dataanalyse.reportmanage.model.ReportModel;
import com.personal.dataanalyse.reportmanage.model.ReportQuota;
import com.personal.dataanalyse.util.DeepCopyUtil;
import com.personal.dataconvert.util.ExcelHtmlUtil;

/**
 * 报表配置App
 * @author cuibo
 *
 */
public class ReportConfigApp
{

	private ReportConfigApp()
	{
		
	}
	
    private static Map<String, DataAnalyseModel> configMap = new HashMap<String, DataAnalyseModel>();

    private static final Object LOCK = new Object();

    /**
     * 注入配置流
     * @param in
     * @throws Exception 
     */
    public static void setConfigStream(InputStream in) throws Exception
    {
        if (in == null)
        {
            return;
        }
        synchronized (ReportConfigApp.LOCK)
        {
            Map<String, DataAnalyseModel> configMap = ReportConfigApp.load(in);
            if (configMap != null && !configMap.isEmpty())
            {
                ReportConfigApp.configMap.putAll(configMap);
            }
        }
    }

    /**
     * 通过配置ID获取DataAnalyseModel
     * @param configId
     * @return
     * @throws Exception 
     */
    public static DataAnalyseModel getWorkBookConfig(String configId) throws Exception
    {
        if (configMap == null)
        {
            throw new Exception("注入的导出配置文件信息为空！");
        }
        return DeepCopyUtil.deepCopy(ReportConfigApp.configMap.get(configId));
    }

    private static Map<String, DataAnalyseModel> load(InputStream in) throws Exception
    {
        try
        {
            XMLDoc doc = new XMLDoc();
            doc.load(in);
            Element root = doc.getRootElement();
            Assert.isNotNull(root, "获取根节点失败！");
            List<Element> modelElements = doc.getChildList(root);
            Map<String, DataAnalyseModel> result = new HashMap<String, DataAnalyseModel>();
            DataAnalyseModel model = null;
            for (Element modelElement : modelElements)
            {
                model = ReportConfigApp.parseModelElement(modelElement, doc);
                if (model != null)
                {
                    result.put(model.getId(), model);
                }
            }
            return result;
        } finally
        {
            ExcelHtmlUtil.release(in);
        }
    }

    /**
     * 解析Model节点
     * @param modelElement
     * @param doc
     */
    private static ReportModel parseModelElement(Element modelElement, XMLDoc doc)
    {
        ReportModel model = new ReportModel();
        String modelId = modelElement.getAttribute("id");
        if (CoreUtil.isEmpty(modelId))
        {
            return null;
        }
        model.setId(modelId);
        model.setModelName(modelElement.getAttribute("modelName"));
//        model.setDataSource(modelElement.getAttribute("dataSource"));
//        model.setProjectType(modelElement.getAttribute("projectType"));
//        model.setCondition(modelElement.getAttribute("condition"));
//        model.setConditionSql(modelElement.getAttribute("conditionSql"));
        model.setTransRowCol(Boolean.valueOf(modelElement.getAttribute("transRowCol")));
        // 修饰节点
        Element decorateElement = XMLDoc.getElementByPath(modelElement, "/decorate");
        if (decorateElement != null)
        {
            parseDecorateElement(decorateElement, model);
        }
        // X维度节点
        Element xGroupFieldsElement = XMLDoc.getElementByPath(modelElement, "/xGroupFields");
        if (xGroupFieldsElement != null)
        {
            parseGroupFieldsElement(xGroupFieldsElement, model, doc, true);
        }
        // Y维度节点
        Element yGroupFieldsElement = XMLDoc.getElementByPath(modelElement, "/yGroupFields");
        if (yGroupFieldsElement != null)
        {
            parseGroupFieldsElement(yGroupFieldsElement, model, doc, false);
        }
        // 普通列
        Element columnFieldsElement = XMLDoc.getElementByPath(modelElement, "/columnFields");
        if (columnFieldsElement != null)
        {
            parseColumnFieldsElement(columnFieldsElement, model, doc, false);
        }
        // 计算列
        Element calFieldsElement = XMLDoc.getElementByPath(modelElement, "/calFields");
        if (calFieldsElement != null)
        {
            parseColumnFieldsElement(calFieldsElement, model, doc, true);
        }
        // 图表节点
        Element echartsElement = XMLDoc.getElementByPath(modelElement, "/echarts");
        if (echartsElement != null)
        {
            parseEchartsElement(echartsElement, model, doc, true);
        }
        return model;
    }

    /**
     * 解析图表节点
     * @param echartsElement
     * @param model
     * @param doc
     * @param b
     */
    private static void parseEchartsElement(Element echartsElement, ReportModel model, XMLDoc doc, boolean b)
    {
        List<Element> echartElements = doc.getChildList(echartsElement);
        if (CoreUtil.isEmpty(echartElements))
        {
            return;
        }
        List<DataChartModel> chartModels = new ArrayList<DataChartModel>();
        for (Element echartElemet : echartElements)
        {
            EChartModel chartModel = getChartByType(echartElemet);
            if (null == chartModel)
            {
                continue;
            }
            chartModel.setChartName(echartElemet.getAttribute("chartName"));
            chartModel.setCharType(echartElemet.getAttribute("charType"));
            chartModel.setChartUnit(echartElemet.getAttribute("chartUnit"));
            chartModel.setChartSubName(echartElemet.getAttribute("chartSubName"));
            Element decorateElement = XMLDoc.getElementByPath(echartElemet, "/decorate");
            // decorate
            if (decorateElement != null)
            {
                chartModel.setColors(decorateElement.getAttribute("colors"));
            }
            // xGroup
            Element xGroupElement = XMLDoc.getElementByPath(echartElemet, "/xGroup");
            if (xGroupElement != null)
            {
                chartModel.setxGroup(xGroupElement.getAttribute("group"));
                chartModel.setxGroupData(xGroupElement.getAttribute("groupData"));
                chartModel.setxGroupDataSql(xGroupElement.getAttribute("groupDataSql"));
            }
            // yGroup
            Element yGroupElement = XMLDoc.getElementByPath(echartElemet, "/yGroup");
            if (yGroupElement != null)
            {
                chartModel.setyGroup(yGroupElement.getAttribute("group"));
                chartModel.setyGroupData(yGroupElement.getAttribute("groupData"));
                chartModel.setyGroupDataSql(yGroupElement.getAttribute("groupDataSql"));
            }
            // field
            Element fieldElement = XMLDoc.getElementByPath(echartElemet, "/field");
            if (fieldElement != null)
            {
                chartModel.setFieldName(fieldElement.getAttribute("fieldName"));
                chartModel.setFieldData(fieldElement.getAttribute("fieldData"));
                chartModel.setFieldDataSql(fieldElement.getAttribute("fieldDataSql"));
            }

            // barline
            Element barLineElement = XMLDoc.getElementByPath(echartElemet, "/barline");
            if (barLineElement != null && chartModel instanceof BarLineEChartModel)
            {
                BarLineEChartModel barLineEChartModel = (BarLineEChartModel) chartModel;
                barLineEChartModel.setAvg(Boolean.valueOf(barLineElement.getAttribute("avg")));
                barLineEChartModel.setMinMax(Boolean.valueOf(barLineElement.getAttribute("minMax")));
                barLineEChartModel.setLineType(barLineElement.getAttribute("lineType"));
                barLineEChartModel.setyDisplayName(barLineElement.getAttribute("yDisplayName"));
            }
            chartModels.add(chartModel);
        }
        model.setChartModels(chartModels);
    }
    
    
    private static EChartModel getChartByType(Element echartElemet)
    {
    	EChartModel chartModel = null;
		if (EchartTypeEnum.折线图.toString().equals(echartElemet.getAttribute("charType"))
				|| EchartTypeEnum.柱状图.toString().equals(echartElemet.getAttribute("charType"))
				|| EchartTypeEnum.折柱图.toString().equals(echartElemet.getAttribute("charType"))
				|| EchartTypeEnum.多维柱图.toString().equals(echartElemet.getAttribute("charType")))
		{
			chartModel = new BarLineEChartModel();
		} else if (EchartTypeEnum.饼图.toString().equals(echartElemet.getAttribute("charType")))
		{
			chartModel = new PieEChartModel();
		}
		return chartModel;
    }
    

    /**
     * 解析普通列或者计算列
     * @param columnFieldsElement
     * @param model
     * @param doc
     * @param isCal
     */
    private static void parseColumnFieldsElement(Element columnFieldsElement, ReportModel model, XMLDoc doc,
            boolean isCal)
    {
        List<Element> columnFieldElements = doc.getChildList(columnFieldsElement);
        if (columnFieldElements == null || columnFieldElements.isEmpty())
        {
            return;
        }
        List<QuotaInfo> columnFields = new ArrayList<QuotaInfo>();
        for (Element element : columnFieldElements)
        {
            ReportQuota quota = new ReportQuota();
            quota.setFieldName(element.getAttribute("fieldName"));
            quota.setDataType(element.getAttribute("fieldDataType"));
            quota.setFieldUnit(element.getAttribute("fieldUnit"));
            quota.setFieldData(element.getAttribute("fieldData"));
            quota.setFieldDataSql(element.getAttribute("fieldDataSql"));
            quota.setFieldOrder(CoreUtil.parseLong(element.getAttribute("fieldOrder")));
            quota.setExpType(Boolean.valueOf(element.getAttribute("expType")));
            quota.setFieldType(element.getAttribute("fieldType"));
            quota.setDotNum(CoreUtil.parseInt(element.getAttribute("dotNum")));
            quota.setTotalType(element.getAttribute("totalType"));
            quota.setCalculateType(element.getAttribute("calculateType"));
            quota.setAlign(element.getAttribute("alignType"));
            quota.setWidth(CoreUtil.parseInt(element.getAttribute("width")));
            columnFields.add(quota);
        }
        if (isCal)
        {
            model.setCalFields(columnFields);
        } else
        {
            model.setColumnFields(columnFields);
        }
    }

    /**
     * 维度
     * @param groupFieldsElement
     * @param model
     * @param doc
     * @param isX
     */
    private static void parseGroupFieldsElement(Element groupFieldsElement, ReportModel model, XMLDoc doc, boolean isX)
    {
        List<Element> xyGroupElements = doc.getChildList(groupFieldsElement);
        if (xyGroupElements == null || xyGroupElements.isEmpty())
        {
            return;
        }
        List<DimensionInfo> list = new ArrayList<DimensionInfo>();
        ReportDimension dimension = null;
        for (Element xyGroup : xyGroupElements)
        {
            dimension = parseXYGroup(model, null, xyGroup, doc, isX);
            if (dimension != null)
            {
                list.add(dimension);
            }
        }
        if (isX)
        {
            model.setxGroupFields(list);
        } else
        {
            model.setyGroupFields(list);
        }
    }

    /**
     * 解析XYGroup
     * @param model
     * @param parent  父亲节点
     * @param xGroup
     * @param doc
     * @param isX
     */
    private static ReportDimension parseXYGroup(ReportModel model, ReportDimension parent, Element xGroup, XMLDoc doc,
            boolean isX)
    {
        // 节点信息
        ReportDimension dimension = new ReportDimension();
        dimension.setGroupName(xGroup.getAttribute("groupName"));
        dimension.setFieldName(xGroup.getAttribute("fieldName"));
        dimension.setDataType(xGroup.getAttribute("fieldDataType"));
        dimension.setFieldUnit(xGroup.getAttribute("fieldUnit"));
        dimension.setFieldData(xGroup.getAttribute("fieldData"));
        dimension.setFieldDataSql(xGroup.getAttribute("fieldDataSql"));
        dimension.setFieldOrder(CoreUtil.parseLong(xGroup.getAttribute("fieldOrder")));
        dimension.setExpType(Boolean.valueOf(xGroup.getAttribute("expType")));
        dimension.setAlign(xGroup.getAttribute("alignType"));
        dimension.setWidth(CoreUtil.parseInt(xGroup.getAttribute("width")));
        dimension.setModel(model);
        // 维度指标
        Element columnFieldsElement = XMLDoc.getElementByPath(xGroup, "/columnFields");
        if (columnFieldsElement != null)
        {
            List<Element> columnFieldElements = doc.getChildList(columnFieldsElement);
            if (columnFieldElements != null && !columnFieldElements.isEmpty())
            {
                List<QuotaInfo> columnFields = new ArrayList<QuotaInfo>();
                for (Element element : columnFieldElements)
                {
                    ReportQuota quota = new ReportQuota();
                    quota.setFieldName(element.getAttribute("fieldName"));
                    quota.setDataType(element.getAttribute("fieldDataType"));
                    quota.setFieldUnit(element.getAttribute("fieldUnit"));
                    quota.setFieldData(element.getAttribute("fieldData"));
                    quota.setFieldDataSql(element.getAttribute("fieldDataSql"));
                    quota.setFieldOrder(CoreUtil.parseLong(element.getAttribute("fieldOrder")));
                    quota.setExpType(Boolean.valueOf(element.getAttribute("expType")));
                    quota.setFieldType(element.getAttribute("fieldType"));
                    quota.setDotNum(CoreUtil.parseInt(element.getAttribute("dotNum")));
                    quota.setTotalType(element.getAttribute("totalType"));
                    quota.setCalculateType(element.getAttribute("calculateType"));
                    quota.setAlign(element.getAttribute("alignType"));
                    quota.setWidth(CoreUtil.parseInt(element.getAttribute("width")));
                    columnFields.add(quota);
                }
                dimension.setColumnFields(columnFields);
            }
        }
        // 计算列
        Element calFieldsElement = XMLDoc.getElementByPath(xGroup, "/calFields");
        if (calFieldsElement != null)
        {
            List<Element> calFieldElements = doc.getChildList(calFieldsElement);
            if (calFieldElements != null && !calFieldElements.isEmpty())
            {
                List<QuotaInfo> calFields = new ArrayList<QuotaInfo>();
                for (Element element : calFieldElements)
                {
                    ReportQuota quota = new ReportQuota();
                    quota.setFieldName(element.getAttribute("fieldName"));
                    quota.setDataType(element.getAttribute("fieldDataType"));
                    quota.setFieldUnit(element.getAttribute("fieldUnit"));
                    quota.setFieldData(element.getAttribute("fieldData"));
                    quota.setFieldDataSql(element.getAttribute("fieldDataSql"));
                    quota.setFieldOrder(CoreUtil.parseLong(element.getAttribute("fieldOrder")));
                    quota.setExpType(Boolean.valueOf(element.getAttribute("expType")));
                    quota.setFieldType(element.getAttribute("fieldType"));
                    quota.setDotNum(CoreUtil.parseInt(element.getAttribute("dotNum")));
                    quota.setTotalType(element.getAttribute("totalType"));
                    quota.setCalculateType(element.getAttribute("calculateType"));
                    quota.setAlign(element.getAttribute("alignType"));
                    quota.setWidth(CoreUtil.parseInt(element.getAttribute("width")));
                    calFields.add(quota);
                }
                dimension.setCalFields(calFields);
            }
        }

        if (parent != null)
        {
            dimension.setParent(parent);
            parent.getChildren().add(dimension);
        }
        getChildren(dimension, model, xGroup, doc, isX);
        return dimension;
    }
    
    
    private static void getChildren(ReportDimension dimension, ReportModel model, Element xGroup, XMLDoc doc, boolean isX)
    {
    	// 递归子节点
    	NodeList children = XMLDoc.getElementsByTagName(xGroup, isX ? "xGroup" : "yGroup");
        if (children != null && children.getLength() > 0)
        {
            for (int i = 0; i < children.getLength(); i++)
            {
                Node node = children.item(i);
                if (!(node instanceof Element))
                {
                    continue;
                }
                parseXYGroup(model, dimension, (Element) node, doc, isX);
            }
        }
    } 
    
    

    /**
     * 装饰节点
     * @param decorateElement
     * @param model
     */
    private static void parseDecorateElement(Element decorateElement, ReportModel model)
    {
//        model.setLeftHead(decorateElement.getAttribute("leftHead"));
//        model.setRightHead(decorateElement.getAttribute("rightHead"));
//        model.setTotalName(decorateElement.getAttribute("totalName"));
    }

}
